/*
 * Copyright 2008 AlphaProject Co., ltd.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * Version 2 as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 * MA 02111-1307 USA
 */

#include <common.h>
#include <command.h>

#include <asm/io.h>

#define PLDR ((volatile unsigned char *)0xFFEF0036)

#define EepClockOn	(*PLDR |=  0x02)
#define EepClockOff	(*PLDR &= ~0x02)
#define EepCsEnable	(*PLDR |=  0x01)
#define EepCsDisable	(*PLDR &= ~0x01)
#define EepDiOn		(*PLDR |=  0x04)
#define EepDiOff	(*PLDR &= ~0x04)
#define EppDoCheck	(*PLDR &   0x20)

static void EEPROM_OutClk(void);
static void EEPROM_SetClk(void);
static void EEPROM_ResetClk(void);
static void EEPROM_SetCs(void);
static void EEPROM_ResetCs(void);
static void EEPROM_SetDi(void);
static void EEPROM_ResetDi(void);
static unsigned short EEPROM_GetDo(void);

static void EEPROM_WriteEnable(void);
static void EEPROM_WriteDisable(void);
//#define udelay(x)
/******************************************************************************
 * EEPROM の読み出し
 *
 *****************************************************************************/
static unsigned short EEPROM_Read(unsigned short addr)
{
	unsigned short ans, i;
  
	ans = 0;
  
	/* 信号の初期化 */
	EEPROM_ResetDi();
	EEPROM_ResetCs();
	EEPROM_ResetClk();
  
	/* CS セット */
	EEPROM_SetCs();
	udelay(1);
	/* SB */
	EEPROM_SetDi();
	EEPROM_OutClk();
  
	/* OP コード 10 */
	EEPROM_SetDi();/* 1 */
	EEPROM_OutClk();
	EEPROM_ResetDi();/* 0 */
	EEPROM_OutClk();
  
	/* アドレス指定 */
	for(i=0; i<6; i++)
	{
		if(addr & 0x0020)
			EEPROM_SetDi();
		else
			EEPROM_ResetDi();
      
		EEPROM_OutClk();
      
		addr <<= 1;
	}
  
	EEPROM_ResetDi();
  
	/* データ取得 */
	for (i = 0; i < 16; i++)
	{
		ans <<= 1;
      
		EEPROM_SetClk();
      
		if (EEPROM_GetDo())
			ans |= 0x0001;
      
		EEPROM_ResetClk();
	}
  
	/* CS クリア */
	EEPROM_ResetCs();
  
	return ans;
}

/*****************************************************************************
 * EEPROM の消去
 *
 ******************************************************************************/
static void EEPROM_Erase(unsigned short addr)
{
	unsigned short i, n;
  
	/* 信号の初期化 */
	EEPROM_ResetDi();
	EEPROM_ResetCs();
	EEPROM_ResetClk();
  
	EEPROM_SetCs();/* CS セット */
  
	/* SB */
	EEPROM_SetDi();
	EEPROM_OutClk();
  
	/* OP コード 11 */
	EEPROM_SetDi();/* 1 */
	EEPROM_OutClk();
	EEPROM_OutClk();/* 1 */
  
	/* アドレス指定 */
	for(i=0; i<6; i++)
	{
		if(addr & 0x0020)
			EEPROM_SetDi();
		else
			EEPROM_ResetDi();
      
		EEPROM_OutClk();
      
		addr <<= 1;
	}
  
	EEPROM_ResetDi();
  
	EEPROM_ResetCs();/* CS クリア */
  
	/* 終了確認 */
	EEPROM_OutClk();
	i = 0x0000;
	EEPROM_SetCs();/* CS セット */
	n = 0;
	while(i == 0x0000)
	{
		EEPROM_OutClk();
		i = EEPROM_GetDo();
		n++;
      
		if(n > 100)
			i = 0x0000;
	}
  
	EEPROM_ResetCs();/* CS クリア */
}

/*****************************************************************************
 * EEPROM の書き込み
 *
 ******************************************************************************/
static void EEPROM_Write(unsigned short addr, unsigned short data)
{
	unsigned short i, n;
  
	/* 信号の初期化 */
	EEPROM_ResetDi();
	EEPROM_ResetCs();
	EEPROM_ResetClk();
  
	EEPROM_SetCs();/* CS セット */
  
	/* SB */
	EEPROM_SetDi();
	EEPROM_OutClk();
  
	/* OP コード 01 */
	EEPROM_ResetDi();/* 0 */
	EEPROM_OutClk();
	EEPROM_SetDi();/* 1 */
	EEPROM_OutClk();
  
	/* アドレス指定 */
	for(i=0; i<6; i++)
	{
		if(addr & 0x0020)
			EEPROM_SetDi();
		else
			EEPROM_ResetDi();
      
		EEPROM_OutClk();
      
		addr <<= 1;
	}
  
	/* データ出力 */
	for(i=0; i<16; i++)
	{
		if(data & 0x8000)
			EEPROM_SetDi();
		else
			EEPROM_ResetDi();
      
		EEPROM_OutClk();
      
		data <<= 1;
	}
	EEPROM_ResetDi();
  
	EEPROM_ResetCs();/* CS クリア */
  
	/* 終了確認 */
	EEPROM_OutClk();
	i = 0x0000;
	EEPROM_SetCs();/* CS セット */
	n = 0;
	while(i == 0x0000)
	{
		EEPROM_OutClk();
		i = EEPROM_GetDo();
		n++;
      
		if(n > 100)
			i = 0x0000;

		if (tstc())
		{
			(void)getc();
			break;
		}
	}
  
	EEPROM_ResetCs();/* CS クリア */
}

/*****************************************************************************
 * EEPROM の全消去
 *
 ******************************************************************************/
static void EEPROM_EraceAll(void)
{
	unsigned short i, n;
  
	/* 信号の初期化 */
	EEPROM_ResetDi();
	EEPROM_ResetCs();
	EEPROM_ResetClk();
  
	EEPROM_SetCs();/* CS セット */
  
	/* SB */
	EEPROM_SetDi();
	EEPROM_OutClk();
  
	/* OP コード 00 */
	EEPROM_ResetDi();
	EEPROM_OutClk();
	EEPROM_OutClk();
  
	/* 全消去コード 100000 */
	EEPROM_SetDi();/* 1 */
	EEPROM_OutClk();
	EEPROM_ResetDi();/* 0 */
	EEPROM_OutClk();
	for(i=0; i<4; i++)
		EEPROM_OutClk();/* 0 x 4 */
  
	EEPROM_ResetCs();/* CS クリア */
  
	/* 終了確認 */
	EEPROM_OutClk();
	i = 0x0000;
	EEPROM_SetCs();/* CS セット */
	n = 0;
	while(i == 0x0000)
	{
		EEPROM_OutClk();
		i = EEPROM_GetDo();
		n++;
      
		if(n > 100)
			i = 0x0000;
	}
  
	EEPROM_ResetCs();/* CS クリア */
}

/*****************************************************************************
 * EEPROM の書き込み許可
 *
 ******************************************************************************/
static  void EEPROM_WriteEnable(void)
{
	unsigned short i;
  
	/* 信号の初期化 */
	EEPROM_ResetDi();
	EEPROM_ResetCs();
	EEPROM_ResetClk();
  
	EEPROM_SetCs();/* CS セット */
  
	/* SB */
	EEPROM_SetDi();
	EEPROM_OutClk();
  
	/* OP コード 00 */
	EEPROM_ResetDi();
	EEPROM_OutClk();
  
	EEPROM_OutClk();
  
	/* 書き込み許可コード 110000 */
	EEPROM_SetDi();/* 1 */
	EEPROM_OutClk();
	EEPROM_OutClk();/* 1 */
	EEPROM_ResetDi();/* 0 */
	for(i=0; i<4; i++)
		EEPROM_OutClk();/* x 4 */
  
	EEPROM_ResetCs();/* CS クリア */
}

/*****************************************************************************
 * EEPROM の書き込み禁止
 *
 ******************************************************************************/
static void EEPROM_WriteDisable(void)
{
	unsigned short i;

	/* 信号の初期化 */
	EEPROM_ResetDi();
	EEPROM_ResetCs();
	EEPROM_ResetClk();
  
	EEPROM_SetCs();/* CS セット */
  
	/* SB */
	EEPROM_SetDi();
	EEPROM_OutClk();
  
	/* OP コード 00 */
	EEPROM_ResetDi();
	EEPROM_OutClk();
	EEPROM_OutClk();
  
	/* 書き込み禁止コード 000000 */
	EEPROM_ResetDi();
	for(i=0; i<6; i++)
		EEPROM_OutClk();/* 0 x 6 */
  
	EEPROM_ResetCs();/* CS クリア */
}

/*****************************************************************************
 * CLK 信号の出力(内部関数)
 *
 ******************************************************************************/
static void EEPROM_OutClk(void)
{
	EEPROM_SetClk();/* CLK のセット */
	EEPROM_ResetClk();/* CLK のクリア */
}

/*****************************************************************************
 * CLK 信号のセット(内部関数)
 *
 ******************************************************************************/
static void EEPROM_SetClk(void)
{
	udelay(250);
	//int_di();
	EepClockOn;
	//int_ei();
	udelay(250);
}

/*****************************************************************************
 * CLK 信号のリセット(内部関数)
 *
 ******************************************************************************/
static void EEPROM_ResetClk(void)
{
	//int_di();
	udelay(250);
	EepClockOff;
	udelay(250);
	//int_ei();
}

/*****************************************************************************
 * CS 信号のセット(内部関数)
 *
 ******************************************************************************/
static void EEPROM_SetCs(void)
{
	//int_di();
	EepCsEnable;
	//int_ei();
}

/*****************************************************************************
 * CS 信号のリセット(内部関数)
 *
 ******************************************************************************/
static void EEPROM_ResetCs(void)
{
	//int_di();
	udelay(250);
	EepCsDisable;
	//int_ei();
}

/*****************************************************************************
 * DI 信号へ 1 を出力(内部関数)
 *
 ******************************************************************************/
static void EEPROM_SetDi(void)
{
	//int_di();
	udelay(250);
	EepDiOn;
	//int_ei();
}

/*****************************************************************************
 * DI 信号へ 0 を出力(内部関数)
 *
 ******************************************************************************/
static void EEPROM_ResetDi(void)
{
	//int_di();
	udelay(250);
	EepDiOff;
	//int_ei();
}

/*****************************************************************************
 * DO 信号の値を取得(内部関数)
 *
 *****************************************************************************/
static unsigned short EEPROM_GetDo(void)
{
	unsigned short ans;
	
	if (EppDoCheck)
		ans = 0x0001;
	else
		ans = 0x0000;
	
	return ans;
}

int eeprom_write(unsigned dev_addr, unsigned offset, 
		 uchar *_buffer, unsigned cnt)
{
	ushort *buffer = (ushort *)_buffer;

	cnt >>= 1;

	while (cnt--)
		EEPROM_Write(offset++, *buffer++);
	
	return 0;
}

int eeprom_read(unsigned dev_addr, unsigned offset,
		uchar *_buffer, unsigned cnt)
{
	ushort *buffer = (ushort *)_buffer;

	cnt >>= 1;
	offset >>= 1;

	while (cnt--)
		*buffer++ = EEPROM_Read(offset++);

	return 0;
}

static int do_read_mac(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
{

	int addr;
	ushort mac[3];

	switch (argc) {
	case 2: {
		int i;
		char *p = argv[1], *end;
		for (i = 0; i < 3; i++)  {
			mac[i] = simple_strtoul(p, &end, 16) << 8;
			p = end + 1;
			mac[i] |= simple_strtoul(p, &end, 16);
			p = end + 1;
		}
		EEPROM_WriteEnable();
		eeprom_write(0, 0, (uchar *)mac, 6);
		EEPROM_WriteDisable();
	}
	default:
		eeprom_read(0, 0, (uchar *)mac, 6);

		printf("%02x:%02x:%02x:%02x:%02x:%02x\n",
		       mac[0] >> 8, mac[0] & 0xff,
		       mac[1] >> 8, mac[1] & 0xff,
		       mac[2] >> 8, mac[2] & 0xff);
	}

	return 1;
}

U_BOOT_CMD(read_mac, 3, 1, do_read_mac, NULL, NULL);
